package com.xd.core.note.impl;

import com.xd.common.constant.SysConstant;
import com.xd.common.exception.XkdException;
import com.xd.common.util.OrderNumUtil;
import com.xd.common.util.RestTemplateUtil;
import com.xd.common.util.RetryUtil;
import com.xd.core.api.dto.SignContractDto;
import com.xd.core.api.dto.SinglePayDto;
import com.xd.core.api.entity.RepaymentOrderInfo;
import com.xd.core.api.mapper.RepaymentOrderInfoMapper;
import com.xd.core.api.service.ApiContractService;
import com.xd.core.api.service.DLApiService;
import com.xd.core.business.entity.Credit;
import com.xd.core.business.entity.IncomingCommonLoan;
import com.xd.core.business.entity.IncomingInfo;
import com.xd.core.business.entity.InsuranceCompanyInfo;
import com.xd.core.business.mapper.OutInfoMapper;
import com.xd.core.business.mapper.RepaymentScheduleMapper;
import com.xd.core.company.entity.CompanyRsa;
import com.xd.core.company.mapper.CompanyRsaMapper;
import com.xd.core.debt.mapper.DebtMapper;
import com.xd.core.note.IAsyncService;
import com.xd.core.note.eventEntity.AssignmentOfDebtEvent;
import com.xd.core.note.eventEntity.DebtEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;


@Slf4j
@Async
@Service
public class AsyncServiceImpl implements IAsyncService {
    @Lazy
    @Autowired
    private DLApiService dlApiService;
    @Autowired
    private RepaymentScheduleMapper repaymentScheduleMapper;
    @Autowired
    private RepaymentOrderInfoMapper repaymentOrderInfoMapper;
    @Autowired
    private ApiContractService apiContractService;
    @Autowired
    private CompanyRsaMapper companyRsaMapper;
    @Autowired
    private DebtMapper debtMapper;
    @Autowired
    private OutInfoMapper outInfoMapper;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RestTemplateUtil restTemplateUtil;
    private static final String REPAY_LOCK_KEY = "lock_repay";
    private static final String DEBT_LOCK_KEY = "lock_debt";

    //判断是否有下一个还款
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void getNext(Integer repayId) {
        Integer outId = repaymentScheduleMapper.getOutId(repayId);
        SinglePayDto singlePayDto = repaymentScheduleMapper.selectSinglePay(outId);
        if (singlePayDto == null) {
            log.info("自动扣款--没有下一期：{}", repayId);
            return;
        }
        repayId = singlePayDto.getOrderId();
        String lockKey = REPAY_LOCK_KEY + repayId;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock(30, TimeUnit.SECONDS);
            //查询是否有申请中的代扣 或者 已支付完成
            Integer i = repaymentOrderInfoMapper.getRepayOrderStatus(repayId);
            if (i > 0) {
                log.info("自动扣款--还款订单已申请或已支付,订单ID: {}", repayId);
                return;
            }
            //插入订单表
            String deptOrder = OrderNumUtil.getRepayOrder();
            RepaymentOrderInfo repaymentOrderInfo = new RepaymentOrderInfo();
            repaymentOrderInfo.setOrderId(singlePayDto.getOrderId());
            repaymentOrderInfo.setDebtId(singlePayDto.getDebtId());
            repaymentOrderInfo.setOrderNo(deptOrder);
            repaymentOrderInfo.setType(SysConstant.DEDUCT_MONEY_TYPE_ONE);
            repaymentOrderInfo.setAmount(Integer.parseInt(singlePayDto.getAmount()));
            repaymentOrderInfo.setCompanyId(singlePayDto.getCompanyId());
            repaymentOrderInfo.setRepayName(singlePayDto.getRepayName());
            repaymentOrderInfo.setIncomingId(singlePayDto.getIncomingId());
            repaymentOrderInfo.setRepayAccount(singlePayDto.getRepayAccount());
            repaymentOrderInfo.setRepayBank(singlePayDto.getRepayBank());
            repaymentOrderInfo.setOutId(outId);
            //插入订单
            repaymentOrderInfoMapper.insert(repaymentOrderInfo);

            singlePayDto.setOrderNo(deptOrder);
            dlApiService.singlePay(singlePayDto, repaymentOrderInfo);
        } catch (Exception e) {
            throw new XkdException(e.getMessage());
        } finally {
            // 确保锁的释放在事务提交之后
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCompletion(int status) {
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                        if (status == TransactionSynchronization.STATUS_COMMITTED) {//成功
                            log.info("事务已提交，自动扣款当期定时任务释放锁，订单ID: {}", singlePayDto.getDebtId());
                        } else {//回滚
                            log.warn("事务已回滚，自动扣款当期定时任务释放锁，订单ID: {}", singlePayDto.getDebtId());
                        }
                    }
                }
            });
        }
    }


    //债权转让确认单
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    @Override
    public void assignmentOfDebtConfirmation(AssignmentOfDebtEvent assignmentOfDebtEvent) {
        List<Integer> debtIds = assignmentOfDebtEvent.getDebtIds();
        log.info("债权转让ID：" + debtIds);
        List<Integer> list = debtMapper.isSameCompany(debtIds);
        if (list.size() > 1)
            throw new XkdException("不能选择多家资方债权");
        for (Integer debtId : debtIds) {
            apiContractService.assignmentOfDebtConfirmation(debtId, assignmentOfDebtEvent.getCompanyId());
        }
    }

    //债权转让通知
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    @Override
    public void zqzrtz(AssignmentOfDebtEvent assignmentOfDebtEvent) {
        List<Integer> debtIds = assignmentOfDebtEvent.getDebtIds();
        List<Integer> list = debtMapper.isSameCompany(debtIds);
        if (list.size() > 1)
            throw new XkdException("不能选择多家资方债权");
        for (Integer debtId : debtIds) {
            apiContractService.zqzrtz(debtId, assignmentOfDebtEvent.getCompanyId());
        }
    }

    //贷款结清证明
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    @Override
    public void dkjqzm(DebtEvent debtEvent) {
//        Integer incomingId = debtEvent.getDebt().getIncomingId();
        Integer outId = debtEvent.getDebt().getOutId();
        outInfoMapper.updateClearDate(outId);
        apiContractService.dkjqzm(outId);
    }

    //担保协议
    @Override
    public void guarantee(Credit credit, IncomingInfo incomingInfo, String dkcontractNo, String dbcontractNo, String Add, List<IncomingCommonLoan> incomingCommonLoans, SignContractDto signContractDto) {
        apiContractService.guarantee(credit, incomingInfo, dkcontractNo, dbcontractNo, Add, incomingCommonLoans, signContractDto);
    }

    //车抵押
    @Override
    public void carmortgage(Integer id, String dkcontractNo, String dycontractNo, IncomingInfo incomingInfo, List<IncomingCommonLoan> incomingCommonLoans, BigDecimal outLoan, String Add, Integer outId) {
        apiContractService.carmortgage(id, dkcontractNo, dycontractNo, incomingInfo, incomingCommonLoans, outLoan, Add, outId);
    }

    //房抵押
    @Override
    public void roommortgage(Integer id, String dkcontractNo, String dycontractNo, IncomingInfo incomingInfo, List<IncomingCommonLoan> incomingCommonLoans, BigDecimal outLoan, String Add, Integer outId) {
        apiContractService.roommortgage(id, dkcontractNo, dycontractNo, incomingInfo, incomingCommonLoans, outLoan, Add, outId);

    }

    //自动代扣还款表本期
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deductionOrder(SinglePayDto singlePayDto) {
        String lockKey = REPAY_LOCK_KEY + singlePayDto.getOrderId();
        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock(30, TimeUnit.SECONDS);
            //查询是否有申请中的代扣 或者 已支付完成
            Integer i = repaymentOrderInfoMapper.getRepayOrderStatus(singlePayDto.getOrderId());
            if (i > 0) {
                log.info("自动扣款--还款订单已申请或已支付,订单ID: {}", singlePayDto.getOrderId());
                return;
            }
            //插入订单表
            String deptOrder = OrderNumUtil.getRepayOrder();
            RepaymentOrderInfo repaymentOrderInfo = new RepaymentOrderInfo();
            repaymentOrderInfo.setOrderId(singlePayDto.getOrderId());
            repaymentOrderInfo.setDebtId(singlePayDto.getDebtId());
            repaymentOrderInfo.setOrderNo(deptOrder);
            repaymentOrderInfo.setType(SysConstant.DEDUCT_MONEY_TYPE_ONE);
            repaymentOrderInfo.setAmount(Integer.parseInt(singlePayDto.getAmount()));
            repaymentOrderInfo.setCompanyId(singlePayDto.getCompanyId());
            repaymentOrderInfo.setRepayName(singlePayDto.getRepayName());
            repaymentOrderInfo.setRepayAccount(singlePayDto.getRepayAccount());
            repaymentOrderInfo.setRepayBank(singlePayDto.getRepayBank());
            repaymentOrderInfo.setIncomingId(singlePayDto.getIncomingId());
            repaymentOrderInfo.setOutId(singlePayDto.getOutId());
            //插入订单
            repaymentOrderInfoMapper.insert(repaymentOrderInfo);

            singlePayDto.setOrderNo(deptOrder);
            dlApiService.singlePay(singlePayDto, repaymentOrderInfo);
        } catch (Exception e) {
            throw new XkdException(e.getMessage());
        } finally {
            // 确保锁的释放在事务提交之后
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCompletion(int status) {
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                        if (status == TransactionSynchronization.STATUS_COMMITTED) {//成功
                            log.info("事务已提交，自动扣款当期定时任务释放锁，订单ID: {}", singlePayDto.getDebtId());
                        } else {//回滚
                            log.warn("事务已回滚，自动扣款当期定时任务释放锁，订单ID: {}", singlePayDto.getDebtId());
                        }
                    }
                }
            });
        }
    }

    //自动代扣债权表 提前还款
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deductionDebt(SinglePayDto singlePayDto) {
        Integer debtId = singlePayDto.getDebtId();
        String lockKey = DEBT_LOCK_KEY + debtId;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock(30, TimeUnit.SECONDS);
            //是否已支付
            Integer i = repaymentOrderInfoMapper.getDebtOrderStatus(debtId);
            if (i > 0) {
                log.info("自动扣款--提前还款订单已申请或已支付,订单ID: {}", debtId);
                return;
            }
            //插入订单表
            String deptOrder = OrderNumUtil.getRepayOrder();
            RepaymentOrderInfo repaymentOrderInfo = new RepaymentOrderInfo();
            repaymentOrderInfo.setDebtId(debtId);
            repaymentOrderInfo.setOrderNo(deptOrder);
            repaymentOrderInfo.setType(SysConstant.DEDUCT_MONEY_TYPE_FOUR);
            repaymentOrderInfo.setAmount(Integer.parseInt(singlePayDto.getAmount()));
            repaymentOrderInfo.setCompanyId(singlePayDto.getCompanyId());
            repaymentOrderInfo.setRepayName(singlePayDto.getRepayName());
            repaymentOrderInfo.setRepayAccount(singlePayDto.getRepayAccount());
            repaymentOrderInfo.setRepayBank(singlePayDto.getRepayBank());
            repaymentOrderInfo.setIncomingId(singlePayDto.getIncomingId());
            repaymentOrderInfo.setOutId(singlePayDto.getOutId());
            //插入订单
            repaymentOrderInfoMapper.insert(repaymentOrderInfo);
            singlePayDto.setOrderNo(deptOrder);
            dlApiService.singlePay(singlePayDto, repaymentOrderInfo);
        } catch (Exception e) {
            throw new XkdException(e.getMessage());
        } finally {
            // 确保锁的释放在事务提交之后
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCompletion(int status) {
                    lock.unlock();
                    if (status == TransactionSynchronization.STATUS_COMMITTED) {
                        log.info("事务已提交，自动扣款提前还款定时任务释放锁，债务ID: {}", debtId);
                    } else {
                        log.warn("事务已回滚，自动扣款提前还款定时任务释放锁，债务ID: {}", debtId);
                    }
                }
            });
        }

    }

    //贷款合同
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void borrowing(SignContractDto signContractDto) {
        apiContractService.borrowing(signContractDto);
    }

    //一次性签署合同
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void signContract(SignContractDto signContractDto) {
        apiContractService.signContract(signContractDto);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void notarizationContract(SignContractDto signContractDto) {
        apiContractService.notarizationContract(signContractDto);
    }

    @Override
    public void extraBorrowing(Integer id, String Y2bank, String Y2BankCard, Integer outId, Credit credit) {
        apiContractService.extraBorrowing(id, Y2bank, Y2BankCard, outId, credit);
    }

    @Override
    public void addSignerBDJKHT(Integer incomingId, String contractNo, IncomingInfo incomingInfo, Integer outId) {
        apiContractService.addSignerBDJKHT(incomingId, contractNo, incomingInfo, outId);
    }

    @Override
    public void addSignerFLWSSDDZHT(Integer incomingId, String contractNo, IncomingInfo incomingInfo, Integer outId) {
        apiContractService.addSignerFLWSSDDZHT(incomingId, contractNo, incomingInfo, outId);
    }

    @Override
    public void addSignerWTDKSQSHT(IncomingInfo incomingInfo, String contractNo, Integer outId) {
        apiContractService.addSignerWTDKSQSHT(incomingInfo, contractNo, outId);
    }

    @Override
    public void addSignerWTTBHT(IncomingInfo incomingInfo, String contractNo, InsuranceCompanyInfo insuranceCompanyInfo, Integer outId) {
        apiContractService.addSignerWTTBHT(incomingInfo, contractNo, insuranceCompanyInfo, outId);
    }

    @Override
    public void addsignerTBSQS(IncomingInfo incomingInfo, String contractNo, Integer outId) {
        apiContractService.addsignerTBSQS(incomingInfo, contractNo, outId);
    }

    /*
     * 通知第三方系统放款结果
     * 响应字符串 "OK"
     * */
    @Override
    public void advanceOutAmount(String orderNo, Integer companyId, String payStatus, String transRemark, String bills) {
        CompanyRsa companyRsa = companyRsaMapper.selectById(companyId);
        String backUrl = companyRsa.getOutBackUrl();
        if (StringUtils.isEmpty(backUrl))
            return;
        HashMap<String, Object> map = new HashMap<>();
        map.put("orderNo", orderNo);//放款商户订单
        map.put("payStatus", payStatus);//状态
        map.put("transRemark", transRemark);//失败原因
        map.put("bills", bills);//账单

        try {
            String result = RetryUtil.executeWithRetry(
                    () -> {
                        ResponseEntity<String> response = restTemplateUtil.postJson(backUrl, map, String.class);
                        return response.getBody();
                    },
                    10,//重试次数
                    new RetryUtil.ExponentialBackoffStrategy(1000, 2) // 1s, 2s, 4s, 8s...
            );
            System.out.println("最终结果: " + result);
        } catch (Exception e) {
            System.out.println("所有重试都失败了: " + e.getMessage());
        }

    }

    /*
     * 通知第三方系统代扣结果
     * 响应字符串 "OK"
     * */
    @Override
    public void advanceSinglePay(RepaymentOrderInfo repaymentOrderInfo, String payStatus, String failCause) {
        CompanyRsa companyRsa = companyRsaMapper.selectById(repaymentOrderInfo.getCompanyId());
        String backUrl = companyRsa.getPayBackUrl();
        if (StringUtils.isEmpty(backUrl))
            return;
        HashMap<String, Object> map = new HashMap<>();
        map.put("payStatus", payStatus);//状态
        map.put("transRemark", failCause);//失败原因
        map.put("orderId", repaymentOrderInfo.getOrderId());//普通账单Id
        map.put("incomingId", repaymentOrderInfo.getIncomingId());//提前还款ID
        String type = repaymentOrderInfo.getType();
        String repayType = "1";
        if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_FOUR) || type.equals(SysConstant.DEDUCT_MONEY_TYPE_FIVE) || type.equals(SysConstant.DEDUCT_MONEY_TYPE_SIX))
            repayType = "2";
        map.put("type", repayType);//还款类型是否是提前还款

        try {
            String result = RetryUtil.executeWithRetry(
                    () -> {
                        ResponseEntity<String> response = restTemplateUtil.postJson(backUrl, map, String.class);
                        return response.getBody();
                    },
                    10,//重试次数 起始时间1秒，乘数2指数增长
                    new RetryUtil.ExponentialBackoffStrategy(1000, 2) // 1s, 2s, 4s, 8s...
            );
            System.out.println("最终结果: " + result);
        } catch (Exception e) {
            System.out.println("所有重试都失败了: " + e.getMessage());
        }
//
    }

}
